/**
* AutoSWF - Cody Cushing. March 26, 2009
* Copyright (c) 2009 Cody Cushing
* No use restrictions. Maintain this notice in all versions. 
**/

package com.autoswf.automator
{
	import com.autoswf.core.ChainLink;
	import com.autoswf.core.Delimiters;
	import com.autoswf.core.Transport;
	
	import flash.events.Event;
	import flash.utils.Dictionary;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;

	/**
	 * Observer class for capturing messages from tree, encoding them and pass them to 
	 * IAutomationClient for persistence or what have you. Allows client to send 
	 * descriptors back which get passed down the tree for remote invocation.
	 */
	public class AutomationObserver extends ChainLink
	{
		private static const NODE_SCHEMA:XML = <node delay="" className="" type="" path=""/>;
		private static const DELIMITER:String = Delimiters.PATH_DELIMITER;
		
		private var clients:Dictionary;
		
		private var lastTime:uint = new Date().time;
		private function get interval():uint
		{
			var newTime:uint = new Date().time;
			var interval:uint = newTime - lastTime;
			lastTime = newTime;
			return interval;
		}
		
		/**
		 * Constructor.
		 */
		public function AutomationObserver(id:String)
		{
			super(id);
			clients = new Dictionary();
		}
		
		
		public function addSubscriber(client:IAutomationClient):void
		{
			clients[client] = client;
			client.remoteLoadCommand = getRemoteLoadCommand();
		}
		
		
		public function hasSubscriber(client:IAutomationClient):Boolean
		{
			return (clients[client] != null)
		}
		
		
		public function removeSubscriber(client:IAutomationClient):void
		{
			delete clients[client];
		}
		
		/**
		 * Serialized an object into an xml descriptor node, adding the interval since the last message.
		 */
		override public function sendUpChain(param:*):void
		{
			var transport:Transport = param;
			
			var node:XML = NODE_SCHEMA;
			
			if(transport.message is Event)
			{
				var event:Event = transport.message;
				node.@className = getQualifiedClassName(event);
				node.@type = event.type;
			}
			node.@path = transport.toString();
			node.@delay = interval;
			
			for each(var client:IAutomationClient in clients)
			{
				client.notify(node.toXMLString());
			}
		}
		
		/**
		 * Deserialized a descriptor node into an object (event) and passes it down the 
		 * tree via its path property.
		 * 
		 * @param descriptor  A stringified xml node of this format
		 * <node className="" type="" path=""/>
		 */
		public function remoteLoad(descriptor:String):void
		{
			var node:XML = XML(descriptor);
			var transport:Transport;
			
			if(node.@className)
			{
				var classDef:Class = Class(getDefinitionByName(node.@className));
				var event:Event = new classDef(node.@type);
				var path:String = node.@path.toString().replace(DELIMITER + node.@type, "");
				transport = new Transport(event, path);
			}
			else
			{
				transport = new Transport(node.@path);
			}
			sendDownChain(transport);
		}
		
		protected function getRemoteLoadCommand():IRemoteLoadCommand
		{
			return new RemoteLoadCommand(this);
		}
	}
}

import com.autoswf.automator.IAutomationClient;
import com.autoswf.automator.IRemoteLoadCommand;
import com.autoswf.automator.AutomationObserver;

internal class RemoteLoadCommand implements IRemoteLoadCommand
{
	private var automationObserver:AutomationObserver;
	public function RemoteLoadCommand(automationObserver:AutomationObserver)
	{
		this.automationObserver = automationObserver;
	}
	
	public function execute(descriptor:String):void
	{
		automationObserver.remoteLoad(descriptor);
	}
}